{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ea058256",
   "metadata": {
    "papermill": {
     "duration": 0.003482,
     "end_time": "2025-05-07T09:28:07.865133",
     "exception": false,
     "start_time": "2025-05-07T09:28:07.861651",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 数据准备"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "3dec05fc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-05-07T09:28:07.872441Z",
     "iopub.status.busy": "2025-05-07T09:28:07.871994Z",
     "iopub.status.idle": "2025-05-07T09:28:23.561937Z",
     "shell.execute_reply": "2025-05-07T09:28:23.560790Z"
    },
    "papermill": {
     "duration": 15.695907,
     "end_time": "2025-05-07T09:28:23.564064",
     "exception": false,
     "start_time": "2025-05-07T09:28:07.868157",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--2025-05-07 09:28:08--  https://www.bbci.de/competition/download/competition_iv/BCICIV_2a_gdf.zip\r\n",
      "Resolving www.bbci.de (www.bbci.de)... 130.149.80.149\r\n",
      "Connecting to www.bbci.de (www.bbci.de)|130.149.80.149|:443... connected.\r\n",
      "HTTP request sent, awaiting response... 200 OK\r\n",
      "Length: 439968864 (420M) [application/zip]\r\n",
      "Saving to: ‘BCICIV_2a_gdf.zip’\r\n",
      "\r\n",
      "BCICIV_2a_gdf.zip   100%[===================>] 419.59M  29.5MB/s    in 15s     \r\n",
      "\r\n",
      "2025-05-07 09:28:23 (28.2 MB/s) - ‘BCICIV_2a_gdf.zip’ saved [439968864/439968864]\r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "# 1. 下载并解压数据集\n",
    "!mkdir -p data/raw_data data/cleaned_data/first_session\n",
    "!wget https://www.bbci.de/competition/download/competition_iv/BCICIV_2a_gdf.zip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "383a7766",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-05-07T09:28:23.578109Z",
     "iopub.status.busy": "2025-05-07T09:28:23.577823Z",
     "iopub.status.idle": "2025-05-07T09:28:31.568721Z",
     "shell.execute_reply": "2025-05-07T09:28:31.567494Z"
    },
    "papermill": {
     "duration": 8.000041,
     "end_time": "2025-05-07T09:28:31.570780",
     "exception": false,
     "start_time": "2025-05-07T09:28:23.570739",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Archive:  BCICIV_2a_gdf.zip\r\n",
      "  inflating: data/raw_data/A01E.gdf  \r\n",
      "  inflating: data/raw_data/A01T.gdf  \r\n",
      "  inflating: data/raw_data/A02E.gdf  \r\n",
      "  inflating: data/raw_data/A02T.gdf  \r\n",
      "  inflating: data/raw_data/A03E.gdf  \r\n",
      "  inflating: data/raw_data/A03T.gdf  \r\n",
      "  inflating: data/raw_data/A04E.gdf  \r\n",
      "  inflating: data/raw_data/A04T.gdf  \r\n",
      "  inflating: data/raw_data/A05E.gdf  \r\n",
      "  inflating: data/raw_data/A05T.gdf  \r\n",
      "  inflating: data/raw_data/A06E.gdf  \r\n",
      "  inflating: data/raw_data/A06T.gdf  \r\n",
      "  inflating: data/raw_data/A07E.gdf  \r\n",
      "  inflating: data/raw_data/A07T.gdf  \r\n",
      "  inflating: data/raw_data/A08E.gdf  \r\n",
      "  inflating: data/raw_data/A08T.gdf  \r\n",
      "  inflating: data/raw_data/A09E.gdf  \r\n",
      "  inflating: data/raw_data/A09T.gdf  \r\n"
     ]
    }
   ],
   "source": [
    "!unzip BCICIV_2a_gdf.zip -d data/raw_data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a3c0fd47",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-05-07T09:28:31.587692Z",
     "iopub.status.busy": "2025-05-07T09:28:31.587350Z",
     "iopub.status.idle": "2025-05-07T09:28:40.531844Z",
     "shell.execute_reply": "2025-05-07T09:28:40.530588Z"
    },
    "papermill": {
     "duration": 8.955203,
     "end_time": "2025-05-07T09:28:40.533962",
     "exception": false,
     "start_time": "2025-05-07T09:28:31.578759",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "%%capture\n",
    "!pip install mne\n",
    "!pip install torch-summary\n",
    "!pip intall einops"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6469ac66",
   "metadata": {
    "papermill": {
     "duration": 0.010936,
     "end_time": "2025-05-07T09:28:40.553360",
     "exception": false,
     "start_time": "2025-05-07T09:28:40.542424",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 模型设计"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6e77afcf",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-05-07T09:28:40.569965Z",
     "iopub.status.busy": "2025-05-07T09:28:40.569595Z",
     "iopub.status.idle": "2025-05-07T09:28:45.094616Z",
     "shell.execute_reply": "2025-05-07T09:28:45.093595Z"
    },
    "papermill": {
     "duration": 4.535686,
     "end_time": "2025-05-07T09:28:45.096413",
     "exception": false,
     "start_time": "2025-05-07T09:28:40.560727",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "from einops import rearrange\n",
    "\n",
    "class EEGConvTransformer(nn.Module):\n",
    "    def __init__(self, num_channels=22, num_classes=4, time_points=1001, \n",
    "                 conv_channels=32, num_heads=4, num_layers=3, dropout=0.5):\n",
    "        super().__init__()\n",
    "        \n",
    "        # 1D CNN部分 - 提取局部特征\n",
    "        self.conv_block = nn.Sequential(\n",
    "            nn.Conv1d(num_channels, conv_channels, kernel_size=31, padding=15),\n",
    "            nn.BatchNorm1d(conv_channels),\n",
    "            nn.ELU(),\n",
    "            nn.MaxPool1d(4),\n",
    "            nn.Dropout(dropout),\n",
    "            \n",
    "            nn.Conv1d(conv_channels, conv_channels*2, kernel_size=15, padding=7),\n",
    "            nn.BatchNorm1d(conv_channels*2),\n",
    "            nn.ELU(),\n",
    "            nn.MaxPool1d(4),\n",
    "            nn.Dropout(dropout),\n",
    "        )\n",
    "        \n",
    "        # Transformer部分 - 捕捉全局依赖\n",
    "        transformer_dim = conv_channels*2\n",
    "        self.pos_embedding = nn.Parameter(torch.randn(1, time_points//16, transformer_dim))\n",
    "        encoder_layer = nn.TransformerEncoderLayer(\n",
    "            d_model=transformer_dim,\n",
    "            nhead=num_heads,\n",
    "            dim_feedforward=transformer_dim*4,\n",
    "            dropout=dropout,\n",
    "            activation='gelu',\n",
    "            batch_first=True\n",
    "        )\n",
    "        self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)\n",
    "        \n",
    "        # 分类头\n",
    "        self.classifier = nn.Sequential(\n",
    "            nn.Linear(transformer_dim, transformer_dim//2),\n",
    "            nn.ELU(),\n",
    "            nn.Dropout(dropout),\n",
    "            nn.Linear(transformer_dim//2, num_classes)\n",
    "        )\n",
    "        \n",
    "    def forward(self, x):\n",
    "        # 输入形状: (batch, 1, channels, time)\n",
    "        x = x.squeeze(1)  # 移除通道维度\n",
    "        \n",
    "        # CNN处理\n",
    "        x = self.conv_block(x)  # (batch, conv_channels*2, time//16)\n",
    "        x = x.permute(0, 2, 1)  # (batch, time//16, conv_channels*2)\n",
    "        \n",
    "        # 添加位置编码\n",
    "        x = x + self.pos_embedding\n",
    "        \n",
    "        # Transformer处理\n",
    "        x = self.transformer(x)\n",
    "        \n",
    "        # 全局平均池化\n",
    "        x = x.mean(dim=1)\n",
    "        \n",
    "        # 分类\n",
    "        return self.classifier(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0e637656",
   "metadata": {
    "papermill": {
     "duration": 0.006796,
     "end_time": "2025-05-07T09:28:45.110668",
     "exception": false,
     "start_time": "2025-05-07T09:28:45.103872",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 数据预处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "81ffe4db",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-05-07T09:28:45.125764Z",
     "iopub.status.busy": "2025-05-07T09:28:45.125341Z",
     "iopub.status.idle": "2025-05-07T09:29:21.226968Z",
     "shell.execute_reply": "2025-05-07T09:29:21.226042Z"
    },
    "papermill": {
     "duration": 36.11079,
     "end_time": "2025-05-07T09:29:21.228297",
     "exception": false,
     "start_time": "2025-05-07T09:28:45.117507",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting EDF parameters from /kaggle/working/data/raw_data/A01T.gdf...\n",
      "GDF file detected\n",
      "Setting channel info structure...\n",
      "Could not determine channel type of the following channels, they will be set as EEG:\n",
      "EEG-Fz, EEG, EEG, EEG, EEG, EEG, EEG, EEG-C3, EEG, EEG-Cz, EEG, EEG-C4, EEG, EEG, EEG, EEG, EEG, EEG, EEG, EEG-Pz, EEG, EEG\n",
      "Creating raw.info structure...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/lib/python3.11/contextlib.py:144: RuntimeWarning: Channel names are not unique, found duplicates for: {'EEG'}. Applying running numbers for duplicates.\n",
      "  next(self.gen)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Reading 0 ... 672527  =      0.000 ...  2690.108 secs...\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-pass filter from 4 - 40 Hz\n",
      "\n",
      "IIR filter parameters\n",
      "---------------------\n",
      "Butterworth bandpass zero-phase (two-pass forward and reverse) non-causal filter:\n",
      "- Filter order 16 (effective, after forward-backward)\n",
      "- Cutoffs at 4.00, 40.00 Hz: -6.02, -6.02 dB\n",
      "\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-stop filter from 49 - 51 Hz\n",
      "\n",
      "FIR filter parameters\n",
      "---------------------\n",
      "Designing a one-pass, zero-phase, non-causal bandstop filter:\n",
      "- Windowed time-domain design (firwin) method\n",
      "- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
      "- Lower passband edge: 49.38\n",
      "- Lower transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 49.12 Hz)\n",
      "- Upper passband edge: 50.62 Hz\n",
      "- Upper transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 50.88 Hz)\n",
      "- Filter length: 1651 samples (6.604 s)\n",
      "\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/A01T.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s\n",
      "/tmp/ipykernel_13/738068932.py:35: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/A01T.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/A01T.fif\n",
      "[done]\n",
      "Extracting EDF parameters from /kaggle/working/data/raw_data/A03T.gdf...\n",
      "GDF file detected\n",
      "Setting channel info structure...\n",
      "Could not determine channel type of the following channels, they will be set as EEG:\n",
      "EEG-Fz, EEG, EEG, EEG, EEG, EEG, EEG, EEG-C3, EEG, EEG-Cz, EEG, EEG-C4, EEG, EEG, EEG, EEG, EEG, EEG, EEG, EEG-Pz, EEG, EEG\n",
      "Creating raw.info structure...\n",
      "Reading 0 ... 660529  =      0.000 ...  2642.116 secs...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/lib/python3.11/contextlib.py:144: RuntimeWarning: Channel names are not unique, found duplicates for: {'EEG'}. Applying running numbers for duplicates.\n",
      "  next(self.gen)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-pass filter from 4 - 40 Hz\n",
      "\n",
      "IIR filter parameters\n",
      "---------------------\n",
      "Butterworth bandpass zero-phase (two-pass forward and reverse) non-causal filter:\n",
      "- Filter order 16 (effective, after forward-backward)\n",
      "- Cutoffs at 4.00, 40.00 Hz: -6.02, -6.02 dB\n",
      "\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-stop filter from 49 - 51 Hz\n",
      "\n",
      "FIR filter parameters\n",
      "---------------------\n",
      "Designing a one-pass, zero-phase, non-causal bandstop filter:\n",
      "- Windowed time-domain design (firwin) method\n",
      "- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
      "- Lower passband edge: 49.38\n",
      "- Lower transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 49.12 Hz)\n",
      "- Upper passband edge: 50.62 Hz\n",
      "- Upper transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 50.88 Hz)\n",
      "- Filter length: 1651 samples (6.604 s)\n",
      "\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/A03T.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s\n",
      "/tmp/ipykernel_13/738068932.py:35: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/A03T.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/A03T.fif\n",
      "[done]\n",
      "Extracting EDF parameters from /kaggle/working/data/raw_data/A04T.gdf...\n",
      "GDF file detected\n",
      "Setting channel info structure...\n",
      "Could not determine channel type of the following channels, they will be set as EEG:\n",
      "EEG-Fz, EEG, EEG, EEG, EEG, EEG, EEG, EEG-C3, EEG, EEG-Cz, EEG, EEG-C4, EEG, EEG, EEG, EEG, EEG, EEG, EEG, EEG-Pz, EEG, EEG\n",
      "Creating raw.info structure...\n",
      "Reading 0 ... 600914  =      0.000 ...  2403.656 secs...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/lib/python3.11/contextlib.py:144: RuntimeWarning: Channel names are not unique, found duplicates for: {'EEG'}. Applying running numbers for duplicates.\n",
      "  next(self.gen)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-pass filter from 4 - 40 Hz\n",
      "\n",
      "IIR filter parameters\n",
      "---------------------\n",
      "Butterworth bandpass zero-phase (two-pass forward and reverse) non-causal filter:\n",
      "- Filter order 16 (effective, after forward-backward)\n",
      "- Cutoffs at 4.00, 40.00 Hz: -6.02, -6.02 dB\n",
      "\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-stop filter from 49 - 51 Hz\n",
      "\n",
      "FIR filter parameters\n",
      "---------------------\n",
      "Designing a one-pass, zero-phase, non-causal bandstop filter:\n",
      "- Windowed time-domain design (firwin) method\n",
      "- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
      "- Lower passband edge: 49.38\n",
      "- Lower transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 49.12 Hz)\n",
      "- Upper passband edge: 50.62 Hz\n",
      "- Upper transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 50.88 Hz)\n",
      "- Filter length: 1651 samples (6.604 s)\n",
      "\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/A04T.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s\n",
      "/tmp/ipykernel_13/738068932.py:35: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/A04T.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/A04T.fif\n",
      "[done]\n",
      "Extracting EDF parameters from /kaggle/working/data/raw_data/A06T.gdf...\n",
      "GDF file detected\n",
      "Setting channel info structure...\n",
      "Could not determine channel type of the following channels, they will be set as EEG:\n",
      "EEG-Fz, EEG, EEG, EEG, EEG, EEG, EEG, EEG-C3, EEG, EEG-Cz, EEG, EEG-C4, EEG, EEG, EEG, EEG, EEG, EEG, EEG, EEG-Pz, EEG, EEG\n",
      "Creating raw.info structure...\n",
      "Reading 0 ... 678979  =      0.000 ...  2715.916 secs...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/lib/python3.11/contextlib.py:144: RuntimeWarning: Channel names are not unique, found duplicates for: {'EEG'}. Applying running numbers for duplicates.\n",
      "  next(self.gen)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-pass filter from 4 - 40 Hz\n",
      "\n",
      "IIR filter parameters\n",
      "---------------------\n",
      "Butterworth bandpass zero-phase (two-pass forward and reverse) non-causal filter:\n",
      "- Filter order 16 (effective, after forward-backward)\n",
      "- Cutoffs at 4.00, 40.00 Hz: -6.02, -6.02 dB\n",
      "\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-stop filter from 49 - 51 Hz\n",
      "\n",
      "FIR filter parameters\n",
      "---------------------\n",
      "Designing a one-pass, zero-phase, non-causal bandstop filter:\n",
      "- Windowed time-domain design (firwin) method\n",
      "- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
      "- Lower passband edge: 49.38\n",
      "- Lower transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 49.12 Hz)\n",
      "- Upper passband edge: 50.62 Hz\n",
      "- Upper transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 50.88 Hz)\n",
      "- Filter length: 1651 samples (6.604 s)\n",
      "\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/A06T.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s\n",
      "/tmp/ipykernel_13/738068932.py:35: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/A06T.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/A06T.fif\n",
      "[done]\n",
      "Extracting EDF parameters from /kaggle/working/data/raw_data/A07T.gdf...\n",
      "GDF file detected\n",
      "Setting channel info structure...\n",
      "Could not determine channel type of the following channels, they will be set as EEG:\n",
      "EEG-Fz, EEG, EEG, EEG, EEG, EEG, EEG, EEG-C3, EEG, EEG-Cz, EEG, EEG-C4, EEG, EEG, EEG, EEG, EEG, EEG, EEG, EEG-Pz, EEG, EEG\n",
      "Creating raw.info structure...\n",
      "Reading 0 ... 681070  =      0.000 ...  2724.280 secs...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/lib/python3.11/contextlib.py:144: RuntimeWarning: Channel names are not unique, found duplicates for: {'EEG'}. Applying running numbers for duplicates.\n",
      "  next(self.gen)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-pass filter from 4 - 40 Hz\n",
      "\n",
      "IIR filter parameters\n",
      "---------------------\n",
      "Butterworth bandpass zero-phase (two-pass forward and reverse) non-causal filter:\n",
      "- Filter order 16 (effective, after forward-backward)\n",
      "- Cutoffs at 4.00, 40.00 Hz: -6.02, -6.02 dB\n",
      "\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-stop filter from 49 - 51 Hz\n",
      "\n",
      "FIR filter parameters\n",
      "---------------------\n",
      "Designing a one-pass, zero-phase, non-causal bandstop filter:\n",
      "- Windowed time-domain design (firwin) method\n",
      "- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
      "- Lower passband edge: 49.38\n",
      "- Lower transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 49.12 Hz)\n",
      "- Upper passband edge: 50.62 Hz\n",
      "- Upper transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 50.88 Hz)\n",
      "- Filter length: 1651 samples (6.604 s)\n",
      "\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/A07T.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s\n",
      "/tmp/ipykernel_13/738068932.py:35: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/A07T.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/A07T.fif\n",
      "[done]\n",
      "Extracting EDF parameters from /kaggle/working/data/raw_data/A09T.gdf...\n",
      "GDF file detected\n",
      "Setting channel info structure...\n",
      "Could not determine channel type of the following channels, they will be set as EEG:\n",
      "EEG-Fz, EEG, EEG, EEG, EEG, EEG, EEG, EEG-C3, EEG, EEG-Cz, EEG, EEG-C4, EEG, EEG, EEG, EEG, EEG, EEG, EEG, EEG-Pz, EEG, EEG\n",
      "Creating raw.info structure...\n",
      "Reading 0 ... 673327  =      0.000 ...  2693.308 secs...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/lib/python3.11/contextlib.py:144: RuntimeWarning: Channel names are not unique, found duplicates for: {'EEG'}. Applying running numbers for duplicates.\n",
      "  next(self.gen)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-pass filter from 4 - 40 Hz\n",
      "\n",
      "IIR filter parameters\n",
      "---------------------\n",
      "Butterworth bandpass zero-phase (two-pass forward and reverse) non-causal filter:\n",
      "- Filter order 16 (effective, after forward-backward)\n",
      "- Cutoffs at 4.00, 40.00 Hz: -6.02, -6.02 dB\n",
      "\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-stop filter from 49 - 51 Hz\n",
      "\n",
      "FIR filter parameters\n",
      "---------------------\n",
      "Designing a one-pass, zero-phase, non-causal bandstop filter:\n",
      "- Windowed time-domain design (firwin) method\n",
      "- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
      "- Lower passband edge: 49.38\n",
      "- Lower transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 49.12 Hz)\n",
      "- Upper passband edge: 50.62 Hz\n",
      "- Upper transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 50.88 Hz)\n",
      "- Filter length: 1651 samples (6.604 s)\n",
      "\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/A09T.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s\n",
      "/tmp/ipykernel_13/738068932.py:35: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/A09T.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/A09T.fif\n",
      "[done]\n",
      "Extracting EDF parameters from /kaggle/working/data/raw_data/A02T.gdf...\n",
      "GDF file detected\n",
      "Setting channel info structure...\n",
      "Could not determine channel type of the following channels, they will be set as EEG:\n",
      "EEG-Fz, EEG, EEG, EEG, EEG, EEG, EEG, EEG-C3, EEG, EEG-Cz, EEG, EEG-C4, EEG, EEG, EEG, EEG, EEG, EEG, EEG, EEG-Pz, EEG, EEG\n",
      "Creating raw.info structure...\n",
      "Reading 0 ... 677168  =      0.000 ...  2708.672 secs...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/lib/python3.11/contextlib.py:144: RuntimeWarning: Channel names are not unique, found duplicates for: {'EEG'}. Applying running numbers for duplicates.\n",
      "  next(self.gen)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-pass filter from 4 - 40 Hz\n",
      "\n",
      "IIR filter parameters\n",
      "---------------------\n",
      "Butterworth bandpass zero-phase (two-pass forward and reverse) non-causal filter:\n",
      "- Filter order 16 (effective, after forward-backward)\n",
      "- Cutoffs at 4.00, 40.00 Hz: -6.02, -6.02 dB\n",
      "\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-stop filter from 49 - 51 Hz\n",
      "\n",
      "FIR filter parameters\n",
      "---------------------\n",
      "Designing a one-pass, zero-phase, non-causal bandstop filter:\n",
      "- Windowed time-domain design (firwin) method\n",
      "- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
      "- Lower passband edge: 49.38\n",
      "- Lower transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 49.12 Hz)\n",
      "- Upper passband edge: 50.62 Hz\n",
      "- Upper transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 50.88 Hz)\n",
      "- Filter length: 1651 samples (6.604 s)\n",
      "\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/A02T.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s\n",
      "/tmp/ipykernel_13/738068932.py:35: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/A02T.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/A02T.fif\n",
      "[done]\n",
      "Extracting EDF parameters from /kaggle/working/data/raw_data/A05T.gdf...\n",
      "GDF file detected\n",
      "Setting channel info structure...\n",
      "Could not determine channel type of the following channels, they will be set as EEG:\n",
      "EEG-Fz, EEG, EEG, EEG, EEG, EEG, EEG, EEG-C3, EEG, EEG-Cz, EEG, EEG-C4, EEG, EEG, EEG, EEG, EEG, EEG, EEG, EEG-Pz, EEG, EEG\n",
      "Creating raw.info structure...\n",
      "Reading 0 ... 686119  =      0.000 ...  2744.476 secs...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/lib/python3.11/contextlib.py:144: RuntimeWarning: Channel names are not unique, found duplicates for: {'EEG'}. Applying running numbers for duplicates.\n",
      "  next(self.gen)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-pass filter from 4 - 40 Hz\n",
      "\n",
      "IIR filter parameters\n",
      "---------------------\n",
      "Butterworth bandpass zero-phase (two-pass forward and reverse) non-causal filter:\n",
      "- Filter order 16 (effective, after forward-backward)\n",
      "- Cutoffs at 4.00, 40.00 Hz: -6.02, -6.02 dB\n",
      "\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-stop filter from 49 - 51 Hz\n",
      "\n",
      "FIR filter parameters\n",
      "---------------------\n",
      "Designing a one-pass, zero-phase, non-causal bandstop filter:\n",
      "- Windowed time-domain design (firwin) method\n",
      "- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
      "- Lower passband edge: 49.38\n",
      "- Lower transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 49.12 Hz)\n",
      "- Upper passband edge: 50.62 Hz\n",
      "- Upper transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 50.88 Hz)\n",
      "- Filter length: 1651 samples (6.604 s)\n",
      "\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/A05T.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s\n",
      "/tmp/ipykernel_13/738068932.py:35: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/A05T.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/A05T.fif\n",
      "[done]\n",
      "Extracting EDF parameters from /kaggle/working/data/raw_data/A08T.gdf...\n",
      "GDF file detected\n",
      "Setting channel info structure...\n",
      "Could not determine channel type of the following channels, they will be set as EEG:\n",
      "EEG-Fz, EEG, EEG, EEG, EEG, EEG, EEG, EEG-C3, EEG, EEG-Cz, EEG, EEG-C4, EEG, EEG, EEG, EEG, EEG, EEG, EEG, EEG-Pz, EEG, EEG\n",
      "Creating raw.info structure...\n",
      "Reading 0 ... 675269  =      0.000 ...  2701.076 secs...\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/lib/python3.11/contextlib.py:144: RuntimeWarning: Channel names are not unique, found duplicates for: {'EEG'}. Applying running numbers for duplicates.\n",
      "  next(self.gen)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-pass filter from 4 - 40 Hz\n",
      "\n",
      "IIR filter parameters\n",
      "---------------------\n",
      "Butterworth bandpass zero-phase (two-pass forward and reverse) non-causal filter:\n",
      "- Filter order 16 (effective, after forward-backward)\n",
      "- Cutoffs at 4.00, 40.00 Hz: -6.02, -6.02 dB\n",
      "\n",
      "Filtering raw data in 1 contiguous segment\n",
      "Setting up band-stop filter from 49 - 51 Hz\n",
      "\n",
      "FIR filter parameters\n",
      "---------------------\n",
      "Designing a one-pass, zero-phase, non-causal bandstop filter:\n",
      "- Windowed time-domain design (firwin) method\n",
      "- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation\n",
      "- Lower passband edge: 49.38\n",
      "- Lower transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 49.12 Hz)\n",
      "- Upper passband edge: 50.62 Hz\n",
      "- Upper transition bandwidth: 0.50 Hz (-6 dB cutoff frequency: 50.88 Hz)\n",
      "- Filter length: 1651 samples (6.604 s)\n",
      "\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/A08T.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s\n",
      "/tmp/ipykernel_13/738068932.py:35: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/A08T.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/A08T.fif\n",
      "[done]\n",
      "Writing /kaggle/working/data/cleaned_data/first_session/First_Session_Subjects.fif\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_13/738068932.py:41: RuntimeWarning: This filename (/kaggle/working/data/cleaned_data/first_session/First_Session_Subjects.fif) does not conform to MNE naming conventions. All raw files should end with raw.fif, raw_sss.fif, raw_tsss.fif, _meg.fif, _eeg.fif, _ieeg.fif, raw.fif.gz, raw_sss.fif.gz, raw_tsss.fif.gz, _meg.fif.gz, _eeg.fif.gz or _ieeg.fif.gz\n",
      "  final_raw.save(new_file_path, overwrite=True)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Closing /kaggle/working/data/cleaned_data/first_session/First_Session_Subjects.fif\n",
      "[done]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[PosixPath('/kaggle/working/data/cleaned_data/first_session/First_Session_Subjects.fif')]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "import mne\n",
    "\n",
    "raw_data_folder = 'data/raw_data/'\n",
    "cleaned_data_folder = 'data/cleaned_data/first_session/'\n",
    "files = os.listdir(raw_data_folder)\n",
    "\n",
    "# Selecting files with suffix 'T.gdf'\n",
    "filtered_files = [file for file in files if file.endswith('T.gdf')]\n",
    "\n",
    "raw_list = []\n",
    "\n",
    "# Iterating through filtered files \n",
    "'''\n",
    "    使用MNE处理EEG数据\n",
    "    1.去眼动(EOG)噪声\n",
    "    2.滤波\n",
    "    3.去基线漂移\n",
    "'''\n",
    "for file in filtered_files:\n",
    "    file_path = os.path.join(raw_data_folder, file)\n",
    "\n",
    "    # Reading raw data\n",
    "    raw = mne.io.read_raw_gdf(file_path, eog=['EOG-left', 'EOG-central', 'EOG-right'], preload=True)\n",
    "    # Droping EOG channels ['EOG-left', 'EOG-central', 'EOG-right'] \n",
    "    raw.drop_channels(['EOG-left', 'EOG-central', 'EOG-right'])\n",
    "\n",
    "    # High Pass Filtering 4-40 Hz  \n",
    "    raw.filter(l_freq=4, h_freq=40, method='iir')\n",
    "    # Notch filter for Removal of Line Voltage\n",
    "  \n",
    "    raw.notch_filter(freqs=50)\n",
    "    # Saving the modified raw data to a file with .fif suffix\n",
    "    new_file_path = os.path.join(cleaned_data_folder, file[:-4] + '.fif')\n",
    "    raw.save(new_file_path, overwrite=True)\n",
    "    # Appending data to the list\n",
    "    raw_list.append(raw)\n",
    "\n",
    "final_raw = mne.concatenate_raws(raw_list)\n",
    "new_file_path = os.path.join(cleaned_data_folder, 'First_Session_Subjects.fif')\n",
    "final_raw.save(new_file_path, overwrite=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "cce77775",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-05-07T09:29:21.271365Z",
     "iopub.status.busy": "2025-05-07T09:29:21.270831Z",
     "iopub.status.idle": "2025-05-07T09:29:21.377500Z",
     "shell.execute_reply": "2025-05-07T09:29:21.376662Z"
    },
    "papermill": {
     "duration": 0.129819,
     "end_time": "2025-05-07T09:29:21.378933",
     "exception": false,
     "start_time": "2025-05-07T09:29:21.249114",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Used Annotations descriptions: ['1023', '1072', '276', '277', '32766', '768', '769', '770', '771', '772']\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'1023': 1,\n",
       " '1072': 2,\n",
       " '276': 3,\n",
       " '277': 4,\n",
       " '32766': 5,\n",
       " '768': 6,\n",
       " '769': 7,\n",
       " '770': 8,\n",
       " '771': 9,\n",
       " '772': 10}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "events = mne.events_from_annotations(final_raw)\n",
    "events[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "7c6c9edd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-05-07T09:29:21.423719Z",
     "iopub.status.busy": "2025-05-07T09:29:21.423403Z",
     "iopub.status.idle": "2025-05-07T09:29:22.743472Z",
     "shell.execute_reply": "2025-05-07T09:29:22.742352Z"
    },
    "papermill": {
     "duration": 1.34419,
     "end_time": "2025-05-07T09:29:22.745274",
     "exception": false,
     "start_time": "2025-05-07T09:29:21.401084",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Not setting metadata\n",
      "2592 matching events found\n",
      "No baseline correction applied\n",
      "0 projection items activated\n",
      "Using data from preloaded Raw for 2592 events and 1001 original time points ...\n",
      "0 bad epochs dropped\n"
     ]
    }
   ],
   "source": [
    "epochs = mne.Epochs(final_raw, events[0], event_id=[7, 8, 9, 10], tmin=0, tmax=4, reject=None, baseline=None, preload=True)\n",
    "# 读取epochs中的数据和标签\n",
    "### implement your code here \n",
    "first_session_data = epochs.get_data(copy=True)\n",
    "first_session_labels = epochs.events[:, -1]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "571975d8",
   "metadata": {
    "papermill": {
     "duration": 0.020866,
     "end_time": "2025-05-07T09:29:22.788651",
     "exception": false,
     "start_time": "2025-05-07T09:29:22.767785",
     "status": "completed"
    },
    "tags": []
   },
   "source": [
    "## 完整训练流程"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "94b6d70e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2025-05-07T09:29:22.834247Z",
     "iopub.status.busy": "2025-05-07T09:29:22.833872Z",
     "iopub.status.idle": "2025-05-07T09:47:10.857627Z",
     "shell.execute_reply": "2025-05-07T09:47:10.856661Z"
    },
    "papermill": {
     "duration": 1068.048041,
     "end_time": "2025-05-07T09:47:10.859522",
     "exception": false,
     "start_time": "2025-05-07T09:29:22.811481",
     "status": "completed"
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/100 | Train Loss: 1.4005 | Train Acc: 0.2634 | Test Acc: 0.2909\n",
      "Epoch 2/100 | Train Loss: 1.3894 | Train Acc: 0.2639 | Test Acc: 0.3141\n",
      "Epoch 3/100 | Train Loss: 1.3850 | Train Acc: 0.2754 | Test Acc: 0.3044\n",
      "Epoch 4/100 | Train Loss: 1.3712 | Train Acc: 0.3020 | Test Acc: 0.3044\n",
      "Epoch 5/100 | Train Loss: 1.3650 | Train Acc: 0.3319 | Test Acc: 0.3295\n",
      "Epoch 6/100 | Train Loss: 1.3479 | Train Acc: 0.3160 | Test Acc: 0.3622\n",
      "Epoch 7/100 | Train Loss: 1.3277 | Train Acc: 0.3594 | Test Acc: 0.4046\n",
      "Epoch 8/100 | Train Loss: 1.2961 | Train Acc: 0.3883 | Test Acc: 0.4759\n",
      "Epoch 9/100 | Train Loss: 1.2666 | Train Acc: 0.3994 | Test Acc: 0.4220\n",
      "Epoch 10/100 | Train Loss: 1.2844 | Train Acc: 0.3917 | Test Acc: 0.4104\n",
      "Epoch 11/100 | Train Loss: 1.2776 | Train Acc: 0.4023 | Test Acc: 0.4605\n",
      "Epoch 12/100 | Train Loss: 1.2348 | Train Acc: 0.4361 | Test Acc: 0.4451\n",
      "Epoch 13/100 | Train Loss: 1.2375 | Train Acc: 0.4192 | Test Acc: 0.4913\n",
      "Epoch 14/100 | Train Loss: 1.2040 | Train Acc: 0.4424 | Test Acc: 0.5010\n",
      "Epoch 15/100 | Train Loss: 1.1907 | Train Acc: 0.4539 | Test Acc: 0.4759\n",
      "Epoch 16/100 | Train Loss: 1.1825 | Train Acc: 0.4510 | Test Acc: 0.4855\n",
      "Epoch 17/100 | Train Loss: 1.1747 | Train Acc: 0.4563 | Test Acc: 0.4933\n",
      "Epoch 18/100 | Train Loss: 1.1606 | Train Acc: 0.4838 | Test Acc: 0.5183\n",
      "Epoch 19/100 | Train Loss: 1.1528 | Train Acc: 0.4727 | Test Acc: 0.5279\n",
      "Epoch 20/100 | Train Loss: 1.1333 | Train Acc: 0.4887 | Test Acc: 0.4913\n",
      "Epoch 21/100 | Train Loss: 1.1319 | Train Acc: 0.4790 | Test Acc: 0.4682\n",
      "Epoch 22/100 | Train Loss: 1.0899 | Train Acc: 0.5070 | Test Acc: 0.5029\n",
      "Epoch 23/100 | Train Loss: 1.1307 | Train Acc: 0.4988 | Test Acc: 0.5202\n",
      "Epoch 24/100 | Train Loss: 1.1199 | Train Acc: 0.4863 | Test Acc: 0.4913\n",
      "Epoch 25/100 | Train Loss: 1.1337 | Train Acc: 0.4867 | Test Acc: 0.5299\n",
      "Epoch 26/100 | Train Loss: 1.1178 | Train Acc: 0.4843 | Test Acc: 0.5067\n",
      "Epoch 27/100 | Train Loss: 1.1066 | Train Acc: 0.4969 | Test Acc: 0.5279\n",
      "Epoch 28/100 | Train Loss: 1.1159 | Train Acc: 0.4949 | Test Acc: 0.5453\n",
      "Epoch 29/100 | Train Loss: 1.0864 | Train Acc: 0.5051 | Test Acc: 0.5318\n",
      "Epoch 30/100 | Train Loss: 1.0964 | Train Acc: 0.4998 | Test Acc: 0.5414\n",
      "Epoch 31/100 | Train Loss: 1.0760 | Train Acc: 0.5171 | Test Acc: 0.5395\n",
      "Epoch 32/100 | Train Loss: 1.0849 | Train Acc: 0.5137 | Test Acc: 0.5125\n",
      "Epoch 33/100 | Train Loss: 1.0763 | Train Acc: 0.5244 | Test Acc: 0.5029\n",
      "Epoch 34/100 | Train Loss: 1.0610 | Train Acc: 0.5258 | Test Acc: 0.5491\n",
      "Epoch 35/100 | Train Loss: 1.0445 | Train Acc: 0.5282 | Test Acc: 0.5202\n",
      "Epoch 36/100 | Train Loss: 1.0346 | Train Acc: 0.5282 | Test Acc: 0.5106\n",
      "Epoch 37/100 | Train Loss: 1.0302 | Train Acc: 0.5412 | Test Acc: 0.5472\n",
      "Epoch 38/100 | Train Loss: 1.0380 | Train Acc: 0.5340 | Test Acc: 0.5318\n",
      "Epoch 39/100 | Train Loss: 1.0373 | Train Acc: 0.5350 | Test Acc: 0.5356\n",
      "Epoch 40/100 | Train Loss: 1.0527 | Train Acc: 0.5340 | Test Acc: 0.5376\n",
      "Epoch 41/100 | Train Loss: 1.0074 | Train Acc: 0.5490 | Test Acc: 0.5202\n",
      "Epoch 42/100 | Train Loss: 1.0198 | Train Acc: 0.5519 | Test Acc: 0.5434\n",
      "Epoch 43/100 | Train Loss: 1.0278 | Train Acc: 0.5466 | Test Acc: 0.5607\n",
      "Epoch 44/100 | Train Loss: 1.0289 | Train Acc: 0.5456 | Test Acc: 0.4894\n",
      "Epoch 45/100 | Train Loss: 1.0187 | Train Acc: 0.5504 | Test Acc: 0.5202\n",
      "Epoch 46/100 | Train Loss: 1.0083 | Train Acc: 0.5697 | Test Acc: 0.5414\n",
      "Epoch 47/100 | Train Loss: 1.0024 | Train Acc: 0.5533 | Test Acc: 0.5183\n",
      "Epoch 48/100 | Train Loss: 0.9872 | Train Acc: 0.5654 | Test Acc: 0.5723\n",
      "Epoch 49/100 | Train Loss: 0.9879 | Train Acc: 0.5625 | Test Acc: 0.5222\n",
      "Epoch 50/100 | Train Loss: 0.9919 | Train Acc: 0.5822 | Test Acc: 0.5723\n",
      "Epoch 51/100 | Train Loss: 0.9518 | Train Acc: 0.5933 | Test Acc: 0.5626\n",
      "Epoch 52/100 | Train Loss: 0.9763 | Train Acc: 0.5586 | Test Acc: 0.5857\n",
      "Epoch 53/100 | Train Loss: 1.0020 | Train Acc: 0.5610 | Test Acc: 0.5588\n",
      "Epoch 54/100 | Train Loss: 0.9817 | Train Acc: 0.5769 | Test Acc: 0.5222\n",
      "Epoch 55/100 | Train Loss: 0.9670 | Train Acc: 0.5822 | Test Acc: 0.5723\n",
      "Epoch 56/100 | Train Loss: 0.9539 | Train Acc: 0.5914 | Test Acc: 0.5491\n",
      "Epoch 57/100 | Train Loss: 0.9622 | Train Acc: 0.5803 | Test Acc: 0.4971\n",
      "Epoch 58/100 | Train Loss: 0.9699 | Train Acc: 0.5779 | Test Acc: 0.5549\n",
      "Epoch 59/100 | Train Loss: 0.9337 | Train Acc: 0.5938 | Test Acc: 0.5626\n",
      "Epoch 60/100 | Train Loss: 0.9577 | Train Acc: 0.5847 | Test Acc: 0.5549\n",
      "Epoch 61/100 | Train Loss: 0.9294 | Train Acc: 0.5909 | Test Acc: 0.5588\n",
      "Epoch 62/100 | Train Loss: 0.9202 | Train Acc: 0.6015 | Test Acc: 0.5857\n",
      "Epoch 63/100 | Train Loss: 0.9216 | Train Acc: 0.6107 | Test Acc: 0.5645\n",
      "Epoch 64/100 | Train Loss: 0.9312 | Train Acc: 0.5929 | Test Acc: 0.5703\n",
      "Epoch 65/100 | Train Loss: 0.9201 | Train Acc: 0.6068 | Test Acc: 0.5549\n",
      "Epoch 66/100 | Train Loss: 0.9279 | Train Acc: 0.6035 | Test Acc: 0.5549\n",
      "Epoch 67/100 | Train Loss: 0.8933 | Train Acc: 0.6237 | Test Acc: 0.5568\n",
      "Epoch 68/100 | Train Loss: 0.9002 | Train Acc: 0.6160 | Test Acc: 0.5356\n",
      "Epoch 69/100 | Train Loss: 0.8875 | Train Acc: 0.6286 | Test Acc: 0.5530\n",
      "Epoch 70/100 | Train Loss: 0.9304 | Train Acc: 0.6151 | Test Acc: 0.5665\n",
      "Epoch 71/100 | Train Loss: 0.8924 | Train Acc: 0.6319 | Test Acc: 0.5491\n",
      "Epoch 72/100 | Train Loss: 0.9313 | Train Acc: 0.6155 | Test Acc: 0.5588\n",
      "Epoch 73/100 | Train Loss: 0.8822 | Train Acc: 0.6324 | Test Acc: 0.5684\n",
      "Epoch 74/100 | Train Loss: 0.8904 | Train Acc: 0.6257 | Test Acc: 0.5780\n",
      "Epoch 75/100 | Train Loss: 0.8736 | Train Acc: 0.6377 | Test Acc: 0.5857\n",
      "Epoch 76/100 | Train Loss: 0.8409 | Train Acc: 0.6416 | Test Acc: 0.5703\n",
      "Epoch 77/100 | Train Loss: 0.8803 | Train Acc: 0.6223 | Test Acc: 0.5588\n",
      "Epoch 78/100 | Train Loss: 0.8683 | Train Acc: 0.6445 | Test Acc: 0.5742\n",
      "Epoch 79/100 | Train Loss: 0.9018 | Train Acc: 0.6122 | Test Acc: 0.5973\n",
      "Epoch 80/100 | Train Loss: 0.8630 | Train Acc: 0.6459 | Test Acc: 0.5684\n",
      "Epoch 81/100 | Train Loss: 0.8512 | Train Acc: 0.6421 | Test Acc: 0.5780\n",
      "Epoch 82/100 | Train Loss: 0.8740 | Train Acc: 0.6276 | Test Acc: 0.5838\n",
      "Epoch 83/100 | Train Loss: 0.8643 | Train Acc: 0.6387 | Test Acc: 0.5491\n",
      "Epoch 84/100 | Train Loss: 0.8562 | Train Acc: 0.6445 | Test Acc: 0.5857\n",
      "Epoch 85/100 | Train Loss: 0.8423 | Train Acc: 0.6430 | Test Acc: 0.5819\n",
      "Epoch 86/100 | Train Loss: 0.8569 | Train Acc: 0.6450 | Test Acc: 0.5780\n",
      "Epoch 87/100 | Train Loss: 0.8301 | Train Acc: 0.6512 | Test Acc: 0.5626\n",
      "Epoch 88/100 | Train Loss: 0.8283 | Train Acc: 0.6556 | Test Acc: 0.5800\n",
      "Epoch 89/100 | Train Loss: 0.8505 | Train Acc: 0.6459 | Test Acc: 0.5780\n",
      "Epoch 90/100 | Train Loss: 0.7977 | Train Acc: 0.6676 | Test Acc: 0.5626\n",
      "Epoch 91/100 | Train Loss: 0.8378 | Train Acc: 0.6503 | Test Acc: 0.5800\n",
      "Epoch 92/100 | Train Loss: 0.8326 | Train Acc: 0.6609 | Test Acc: 0.6089\n",
      "Epoch 93/100 | Train Loss: 0.8263 | Train Acc: 0.6561 | Test Acc: 0.5857\n",
      "Epoch 94/100 | Train Loss: 0.8436 | Train Acc: 0.6493 | Test Acc: 0.5761\n",
      "Epoch 95/100 | Train Loss: 0.8663 | Train Acc: 0.6454 | Test Acc: 0.5742\n",
      "Epoch 96/100 | Train Loss: 0.8464 | Train Acc: 0.6585 | Test Acc: 0.5511\n",
      "Epoch 97/100 | Train Loss: 0.8318 | Train Acc: 0.6459 | Test Acc: 0.5780\n",
      "Epoch 98/100 | Train Loss: 0.8094 | Train Acc: 0.6609 | Test Acc: 0.5877\n",
      "Epoch 99/100 | Train Loss: 0.8181 | Train Acc: 0.6570 | Test Acc: 0.5530\n",
      "Epoch 100/100 | Train Loss: 0.7935 | Train Acc: 0.6744 | Test Acc: 0.5761\n",
      "Best Test Accuracy: 0.6089\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "from sklearn.model_selection import train_test_split\n",
    "from torch.utils.data import TensorDataset, DataLoader\n",
    "\n",
    "# 数据准备 (使用您提供的预处理数据)\n",
    "X = first_session_data  # (2592, 22, 1001)\n",
    "y = first_session_labels - np.min(first_session_labels)  # 归一化标签到0-3\n",
    "\n",
    "# 标准化\n",
    "X = (X - np.mean(X)) / np.std(X)\n",
    "\n",
    "# 分割数据集\n",
    "X_train, X_test, y_train, y_test = train_test_split(\n",
    "    X, y, test_size=0.2, random_state=42, stratify=y\n",
    ")\n",
    "\n",
    "# 转换为Tensor\n",
    "X_train = torch.FloatTensor(X_train).unsqueeze(1)  # 添加通道维度\n",
    "X_test = torch.FloatTensor(X_test).unsqueeze(1)\n",
    "y_train = torch.LongTensor(y_train)\n",
    "y_test = torch.LongTensor(y_test)\n",
    "\n",
    "# 创建DataLoader\n",
    "train_dataset = TensorDataset(X_train, y_train)\n",
    "test_dataset = TensorDataset(X_test, y_test)\n",
    "\n",
    "batch_size = 64\n",
    "train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\n",
    "test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)\n",
    "\n",
    "# 训练设置\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "model = EEGConvTransformer().to(device)\n",
    "optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-4)\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "\n",
    "# 训练函数\n",
    "def train(model, loader, optimizer, criterion):\n",
    "    model.train()\n",
    "    total_loss, total_correct = 0, 0\n",
    "    \n",
    "    for inputs, labels in loader:\n",
    "        inputs, labels = inputs.to(device), labels.to(device)\n",
    "        \n",
    "        optimizer.zero_grad()\n",
    "        outputs = model(inputs)\n",
    "        loss = criterion(outputs, labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        total_loss += loss.item()\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        total_correct += (predicted == labels).sum().item()\n",
    "    \n",
    "    return total_loss / len(loader), total_correct / len(loader.dataset)\n",
    "\n",
    "# 测试函数\n",
    "def evaluate(model, loader):\n",
    "    model.eval()\n",
    "    total_correct = 0\n",
    "    all_preds, all_labels = [], []\n",
    "    \n",
    "    with torch.no_grad():\n",
    "        for inputs, labels in loader:\n",
    "            inputs, labels = inputs.to(device), labels.to(device)\n",
    "            outputs = model(inputs)\n",
    "            _, predicted = torch.max(outputs, 1)\n",
    "            total_correct += (predicted == labels).sum().item()\n",
    "            all_preds.extend(predicted.cpu().numpy())\n",
    "            all_labels.extend(labels.cpu().numpy())\n",
    "    \n",
    "    return total_correct / len(loader.dataset), all_preds, all_labels\n",
    "\n",
    "# 训练循环\n",
    "epochs = 100\n",
    "best_acc = 0\n",
    "\n",
    "for epoch in range(epochs):\n",
    "    train_loss, train_acc = train(model, train_loader, optimizer, criterion)\n",
    "    test_acc, _, _ = evaluate(model, test_loader)\n",
    "    \n",
    "    if test_acc > best_acc:\n",
    "        best_acc = test_acc\n",
    "        torch.save(model.state_dict(), 'best_model.pth')\n",
    "    \n",
    "    print(f\"Epoch {epoch+1}/{epochs} | Train Loss: {train_loss:.4f} | \"\n",
    "          f\"Train Acc: {train_acc:.4f} | Test Acc: {test_acc:.4f}\")\n",
    "\n",
    "print(f\"Best Test Accuracy: {best_acc:.4f}\")"
   ]
  }
 ],
 "metadata": {
  "kaggle": {
   "accelerator": "none",
   "dataSources": [],
   "dockerImageVersionId": 31012,
   "isGpuEnabled": false,
   "isInternetEnabled": true,
   "language": "python",
   "sourceType": "notebook"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.11"
  },
  "papermill": {
   "default_parameters": {},
   "duration": 1150.80756,
   "end_time": "2025-05-07T09:47:13.837549",
   "environment_variables": {},
   "exception": null,
   "input_path": "__notebook__.ipynb",
   "output_path": "__notebook__.ipynb",
   "parameters": {},
   "start_time": "2025-05-07T09:28:03.029989",
   "version": "2.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
